#!/usr/bin/env python3
"""智能后端启动脚本 - 自动检测并清理端口冲突"""

import subprocess
import os
import sys
import time
import re
from pathlib import Path

PORT = 8000
MAX_KILL_RETRIES = 3
CANDIDATE_PORTS = [8000, 8001, 8010, 8080]


def _parse_listening_pids(netstat_output: str) -> set[str]:
    """仅解析 LISTENING 状态对应的 PID 集合。"""
    pids: set[str] = set()
    for line in netstat_output.splitlines():
        # 仅处理 LISTENING 行，避免 ESTABLISHED/TIME_WAIT 干扰
        if "LISTENING" in line.upper():
            m = re.search(r"\s(\d+)$", line)
            if m:
                pids.add(m.group(1))
    return pids


def check_and_kill_port(port: int) -> int:
    """检查端口占用并尽力清理 LISTENING 进程（多次重试）。

    仅当环境变量 FORCE_KILL=1 时才执行清理，避免误杀正常进程。
    """
    if os.environ.get("FORCE_KILL", "0") != "1":
        return 0
    try:
        total_killed = 0
        for attempt in range(1, MAX_KILL_RETRIES + 1):
            result = subprocess.run(
                f"netstat -ano | findstr :{port}",
                shell=True,
                capture_output=True,
                text=True,
                timeout=5,
            )

            listening_pids = _parse_listening_pids(result.stdout)

            if not listening_pids:
                if attempt == 1:
                    print(f"[√] 端口 {port} 空闲")
                return total_killed

            if attempt == 1:
                print(f"\n[!] 检测到端口 {port} 被 {len(listening_pids)} 个 LISTENING 进程占用")
            else:
                print(
                    f"[!] 第 {attempt} 次重试，剩余 LISTENING 进程: {', '.join(sorted(listening_pids))}"
                )

            for pid in list(listening_pids):
                try:
                    subprocess.run(
                        f"taskkill /F /PID {pid}",
                        shell=True,
                        capture_output=True,
                        timeout=5,
                    )
                    total_killed += 1
                    print(f"    已终止进程 PID={pid}")
                except Exception:
                    print(f"    无法终止进程 PID={pid}")

            time.sleep(2)  # 等待端口释放

        # 重试后再输出一次诊断内容
        diag = subprocess.run(
            f"netstat -ano | findstr :{port}",
            shell=True,
            capture_output=True,
            text=True,
            timeout=5,
        )
        remain = _parse_listening_pids(diag.stdout)
        if remain:
            print("\n[!] 仍然存在占用该端口的 LISTENING 进程，诊断输出:")
            for line in diag.stdout.splitlines():
                if ":" + str(port) in line:
                    print("    " + line)
        return total_killed
    except Exception as e:
        print(f"[!] 检查端口时出错: {e}")
        return 0


def verify_port_free(port: int) -> bool:
    """验证端口是否真正空闲（无 LISTENING 条目）。"""
    try:
        result = subprocess.run(
            f"netstat -ano | findstr :{port}",
            shell=True,
            capture_output=True,
            text=True,
            timeout=3,
        )
        return len(_parse_listening_pids(result.stdout)) == 0
    except Exception:
        return False


def start_uvicorn(selected_port: int):
    """启动Uvicorn服务器"""
    # 切换到backend目录
    backend_dir = Path(__file__).parent.parent / "backend"

    # 确定正确的Python解释器（必须使用虚拟环境）
    python_exe = sys.executable
    venv_python = backend_dir / "venv" / "Scripts" / "python.exe"
    parent_venv_python = Path(__file__).parent.parent / ".venv" / "Scripts" / "python.exe"

    # 优先使用虚拟环境Python
    if venv_python.exists():
        python_exe = str(venv_python)
    elif parent_venv_python.exists():
        python_exe = str(parent_venv_python)

    print(f"\n[*] 启动目录: {backend_dir}")
    print(f"[*] Python: {python_exe}")
    print(f"[*] 端口: {selected_port}")
    print("[*] 启动中...\n")

    # 启动uvicorn
    try:
        subprocess.run(
            [
                python_exe,
                "-m",
                "uvicorn",
                "app.main:app",
                "--host",
                "127.0.0.1",
                "--port",
                str(selected_port),
                "--reload",
            ],
            cwd=backend_dir,
        )
    except KeyboardInterrupt:
        print("\n\n[*] 服务已停止")
    except Exception as e:
        print(f"\n[!] 启动失败: {e}")
        sys.exit(1)


def main():
    print("=" * 60)
    print("后端服务智能启动器 v1.0")
    print("=" * 60)

    # 1) 构建候选端口列表：优先使用环境变量 PORT
    env_port = os.environ.get("PORT")
    candidate_ports = CANDIDATE_PORTS.copy()
    if env_port and env_port.isdigit():
        preferred = int(env_port)
        if preferred in candidate_ports:
            candidate_ports.remove(preferred)
        candidate_ports.insert(0, preferred)

    # 2) 可选清理：仅当 FORCE_KILL=1 时尝试清理首选端口
    killed = check_and_kill_port(candidate_ports[0])
    if killed > 0:
        print(f"\n[√] 已清理 {killed} 个旧进程")

    # 3) 选择第一个空闲端口
    selected_port = None
    for p in candidate_ports:
        if verify_port_free(p):
            selected_port = p
            break

    if selected_port is None:
        print(f"\n[X] 没有可用端口（候选: {', '.join(map(str, candidate_ports))}）")
        print("请手动释放端口或设置环境变量 PORT 使用自定义端口")
        print("诊断建议: netstat -ano | findstr :<port> 与 PowerShell Get-NetTCPConnection")
        sys.exit(1)

    # 4) 启动服务
    start_uvicorn(selected_port)


if __name__ == "__main__":
    main()
